/**
 * BlogWidget Configuration
 * 1. Global ustawienia widgeta
 *
 * ekcal_turnOff = true/false - włącza lub wyłącza przyciski
 *
 * ekcal_type - typ widgeta {blogger, blox, bloog, generic} - deklaracja platformy blogowej pozwala na używanie predifionwanych ustawień
 *
 * ekcal_minCount - minimalna ilość elementów w liście <ul> dla której dodawny jest przycisk, default = 3
 * ekcal_iframeWidth - szerokośc widgetu w panelu bocznym
 * ekcal_hideSideBar - boolean - jeśli true nie generuj widgetu w panelu bocznym 
 *
 *
 * 2. Konfiguracja widgeta w trybie pracy GENERIC
 * Prioryteyzacja parametrów zgodna jest z dokładnością definiowanych przez nie elemnety
 * n.p. DOM node id - wskazuje element jednoznacznie - zgodnie ze specyfikacją HTML w dokumencie powinien znajdować się dokładnie jeden 
 * element o określonym id, elementy określane przez class/tag mogą wsytępować wielokrotnie w całym dokumencie
 * Gdy dla wskazania danego rodzaju elementów zdefiniowane są zarówno id or class/tag wówczas element wskazany przez id zostanie 
 * użyty dla celów procesowania.
 *
 * 2.1 Wykrywanie Postów/Wpisów
 * -> Bardzo często treść bloga opakowana jest w jeden element DOM, dzięki czemy może zostać skutecznie odseparowany od 
 * -> headerów, footerów czy sidebarów. 
 * -> Korzystając z tego możemy zawęzić obszar procesowania, unikając umieszczania przycisku w nieodpowiedniej sekcji.
 *
 * ekcal_generic_contentId - id głównej kolumny z treścią - zawierającej wszystkie wpisy
 * ekcal_generic_contentClass - klasa wskazująca główną kolumnę //TODO: implement
 * ekcal_generic_contentTag - tag kolumny głównej //TODO: implement
 *
 * ekcal_generic_contentAsEntry - (default=false) - treat main column content as the whole entry
 *		comment - used in one case for a single entry view (single entry on page,without a further structure inside)
 *
 * -> Poniższe patametry definiują element opakowujący pojedynczy wpis. Najczęściej zawiera w sobie header wpisu, footer wpisu 
 * -> w zależności od platfromy blogowej.
 *
 * ekcal_generic_entryClass - klasa wskazująca element opakowujący pojedynczy wpis
 * ekcal_generic_entryTag - tag elementu opakowującego pojedynczy wpis
 *
 * 2.2 Przetwarzanie Postów/Wpisów
 * 2.2.1 Treść Wpisu
 * -> Poniższy pametr pozwala na wskazanie treści wpisu wewnątrz elementu opakowującego.  
 * -> Jeśli nie jest zdefiniowany wówczas cała zawartość wprapper jest procesowana jako treść wpisu.
 *
 * ekcal_generic_entryContentClass - klasa elementu zawierającego treść wpisu 
 * ekcal_generic_entryContentTag - tag elementu zawierającego treść wpisu
 *
 * 2.2.2 Header Wpisu
 * -> Nagłówki wpisu często zawierają informacje o tytule, adresie url itp. które chcemy wydobyć
 *
 * ekcal_generic_entryHeaderClass - klasa nagówka wpisu
 * ekcal_generic_entryHeaderTag - tag nagłówka wpisu
 *
 * -> Poniższe parametry pozwalają na wskzanie elementu zaiwerającego tytuł wpisu
 *
 * ekcal_generic_entryHeaderTitleClass - klasa elementu zawierającego tytuł wpisu
 * ekcal_generic_entryHeaderTitleTag - tag elementu zawierającego tytuł wpisu
 *
 * -> Pniższe patametry definiują element zawierający adres URL - najlepiej aby prowadził on do widoku pojedynczego wpisu
 *
 * ekcal_generic_entryHeaderUrlWrapperClass - klasa wskazująca na wraper adresu
 * ekcal_generic_entryHeaderUrlWrapperTag - tag wskazujący na wraper adresu
 *
 * ekcal_generic_entryHeaderUrlClass - klasa elementu zawierającego adres url wpisu tag to zawsze <A>
 *											 jeśli klasa nie jest zdefiniowana wówczas następuje wyszukiwanie po samym tagu
 *
 * 2.2.3 Post Footer
 * -> Stoka wpisu często zawiera informacje które chcielibyśmy wydobyć n.p. tagi, adres url
 *
 * ekcal_generic_entryFooterClass - klasa stopki wpisu
 * ekcal_generic_entryFooterTag - tag stopki wpisu
 *
 * //poniższe parametry są używane jedynie gdy odpowiadające parametry nie zostały zdefiniowane dla nagłówka
 *
 * ekcal_generic_entryFooterUrlWrapperClass - klasa wskazująca na wraper adresu
 * ekcal_generic_entryFooterUrlWrapperTag - tag wskazujący na wraper adresu
 *
 * ekcal_generic_entryFooterUrlClass - klasa elementu zawierającego adres url wpisu tag to zawsze <A>
 *											 jeśli klasa nie jest zdefiniowana wówczas następuje wyszukiwanie po samym tagu
 *
 *
 */


/**
 * removes all the empty characters form the begining and the end of the sring str
 * @param {string} str - string to process
 */
function EkcalTrim(str) {
	var trimmed = str

	if(trimmed) {
		trimmed = trimmed.replace(/^\s+|\s+$/g, '');
	}

	return trimmed;
}

/**	
 * estimatest number of words in the string
 * @param {string} str
 */
function EkcalWordCount(str) {
	var wcregex = /(^|\s)[^\s]+?(?=\s|$)/ig;

	var matches = str.match(wcregex);

	if(matches) {
		return matches.length;
	} else {
		return 0;
	}
}

function EkcalReplaceWhitespaces(str, char) {
	if (str != null) {
		return str.replace(/ +/g,char);
	} else {
		return str;
	}
}

function EkcalStartsWith(str, prefix) {
	return str != null && (str.match("^"+prefix)==prefix);
}

function EkcalReverse(text) {
	var out = "";
	for (var i = text.length - 1; i >= 0; i--) {
		out += text.charAt(i);
	}
	return out;
}

function EkcalRemoveEmptyLines(str) {
	var out = "";
	var lines = str.split('\n');
	for (var i = 0; i < lines.length; i++) {
		var trim = EkcalTrim(lines[i]);
		if (trim != null && trim.length > 0) {
			if (out.length > 0) {
				out += '\n';
			}
			out += trim;
		}
	}
	return out;
}


function $EkcalTry(){
	for (var i = 0, l = arguments.length; i < l; i++){
		try {
			return arguments[i]();
		} catch(e){}
	}
	return null;
};


function EkcalRandomString() {
	var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
	var randomstring = '';
	for(var i = 0; i < 8; i++) {
		var rnum = Math.floor(Math.random() * chars.length);
		randomstring += chars.substring(rnum, rnum + 1);
	}
	return randomstring;
}


/**
 * http://code.google.com/p/microajax/
 * 
 * @param url
 * @param callbackFunction
 * @returns {microAjax}
 */
function EkcalMicroAjax(url)
{
	this.getRequest = function() {
		return $EkcalTry(function(){
			return new XMLHttpRequest();
		}, function(){
			return new ActiveXObject('MSXML2.XMLHTTP');
		}, function(){
			return new ActiveXObject('Microsoft.XMLHTTP');
		});
	};

	this.postBody = (arguments[1] || "");
	this.request = this.getRequest();
	
	if(this.request) {
		var req = this.request;
		req.onreadystatechange = function() {
			if(req.readyState == 4){
				EkcalAjaxResponse(req);
			}
		}
		if (this.postBody!=="") {
			req.open("POST", url, true);
			req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
			req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
			req.setRequestHeader('Connection', 'close');
		} else {
			req.open("GET", url, true);
		}

		req.send(this.postBody);
	}
}

var EkcalElementMap = {};

function EkcalAjaxResponse(req) {
	//alert("response:");
	/*if (!req.responseText || req.responseText.length <= 10) {
		return;
	}*/
	var xml = $EkcalTry(function(){
		return req.responseXML;
	});
	if (xml == null) {
		return;
	}
	var docEl = xml.documentElement;
	var id = EkcalGetFirstElementText(docEl, "id");
	var position = EkcalGetFirstElementText(docEl, "position", "10");
	var align = EkcalGetFirstElementText(docEl, "align", "center");
	var width = EkcalGetFirstElementText(docEl, "width");
	var height = EkcalGetFirstElementText(docEl, "height");
	var src = EkcalGetFirstElementText(docEl, "src");
	//alert("id:"+id+"\nposition:"+position+"\nalign:"+align+"\nwidth:"+width+"\nheight:"+height+"\nsrc:"+src);
	if (id == null && width == null && height == null && src == null) {
		return;
	}
	var nodes = EkcalElementMap[id];
	if (nodes == null) {
		return;
	}
	
	var frame = document.createElement("iframe");
	frame.width = width+"px";
	frame.height = height +"px";
	frame.src = src;
	frame.setAttribute("frameborder", "0");
	frame.setAttribute("allowTransparency", "true");
	
	if (position == "11") {
		//na prawo od składników
		var canInsertWidget = true;
		
		if (nodes[0].nodeType == 1 && nodes[0].nodeName.toLowerCase() == "ul" && nodes.length > 1) {
			//get all nodes between
			var nodesBetween = EkcalGetNodesBetween(nodes[0], nodes[nodes.length - 1]);
			if (nodesBetween == null) {
				canInsertWidget = false;
			} else {
				nodes = nodesBetween;
			}
		}
		
		if (canInsertWidget) {
			//check if there an image within nodes
			for (var i = 0; i < nodes.length; i++) {
				if (EkcalHasImageWithin(nodes[i])) {
					canInsertWidget = false;
					break;
				}
			}
			
		}
		if (canInsertWidget) {
			var mainDiv = document.createElement("div");
			mainDiv.style.width = "100%";
			var leftDiv = document.createElement("div");
			leftDiv.style.cssFloat = "left";
			leftDiv.style.styleFloat = "left";
			var rightDiv = document.createElement("div");
			rightDiv.style.cssFloat = "right";
			rightDiv.style.styleFloat = "right";
			mainDiv.appendChild(leftDiv);
			mainDiv.appendChild(rightDiv);
			var bottomDiv = document.createElement("div");
			bottomDiv.style.clear = "both";
			var firstNode = nodes[0];
			firstNode.parentNode.insertBefore(mainDiv, firstNode);
			firstNode.parentNode.insertBefore(bottomDiv, firstNode);
			for (var i = 0; i < nodes.length; i++) {
				var node = nodes[i];
				//node.parentNode.removeChild(node);
				leftDiv.appendChild(node);
			}
			rightDiv.appendChild(frame);
		} else {
			position = "10";
			align = "center";
		}
	}
	
	if (position != "11") {
		if (align == "center") {
			var center = document.createElement("center");
			center.appendChild(frame);
			frame = center;
		} else {
			frame.align = align;
			var div = document.createElement("div");
			div.appendChild(frame);
			frame = div;
		}
		if (position == "20" || position == "21" || position == "22") {
			//obliczanie widełek 
			var prevGroupNode = null;
			var nextGroupNode = null;
			var prevGroupId = EkcalElementMap[id+"prev"];
			var nextGroupId = EkcalElementMap[id+"next"];
			if (prevGroupId != null) {
				var prevGroupNodes = EkcalElementMap[prevGroupId];
				if (prevGroupNodes != null) {
					for (var i = prevGroupNodes.length - 1; i >= 0; i--) {
						if (prevGroupNodes[i].nodeType == 1) {
							prevGroupNode = prevGroupNodes[i];
							break;
						}
					}
				}
			}
			if (nextGroupId != null) {
				var nextGroupNodes = EkcalElementMap[nextGroupId];
				if (nextGroupNodes != null) {
					for (var i = 0; i < nextGroupNodes.length; i++) {
						if (nextGroupNodes[i].nodeType == 1) {
							nextGroupNode = nextGroupNodes[i];
							break;
						}
					}
				}
			}
			var imgContainer = null;
			var lastNode = nodes[nodes.length - 1];
			//only for blogger
			 var postDiv = EkcalGetAncestor(lastNode, "post");
			 if (postDiv == null || prevGroupId != null || nextGroupId != null /*gdy jest więcej niż 1 widżet to zamień na pod*/) {
				 position = "10";
			 } else {
				 var imgs = postDiv.getElementsByTagName('img');
				 var imgsArr = new Array();
				 if (imgs != null && imgs.length > 0) {
					 for (var i = 0; i < imgs.length; i++) {
						 if (imgs[i].width > 100) {
							 imgsArr.push(imgs[i]);
						 }
					 }
				 }
				 if (imgsArr.length > 0) {
					 var target = null;
					 if (position == "20") { //pod pierwszym zdjęciem, ale poniżej wcześniejszej grupy
						 for (var i = 0; i < imgsArr.length; i++) {
							 var container = EkcalGetAncestorChild(imgsArr[i], lastNode.parentNode);
							 if (container != null) {
								 if (prevGroupNode == null || (prevGroupNode.parentNode == container.parentNode && prevGroupNode.offsetTop < container.offsetTop)) {
									 imgContainer = container;
									 break;
								 }
							 }
						 }
					 } else if (position == "22") { //pod ostatnim zdjęciem, ale powyżej następnej grupy
						 for (var i = imgsArr.length - 1; i >= 0; i--) {
							 var container = EkcalGetAncestorChild(imgsArr[i], lastNode.parentNode);
							 if (container != null) {
								 if (nextGroupNode == null || (nextGroupNode.parentNode == container.parentNode && nextGroupNode.offsetTop > container.offsetTop)) {
									 imgContainer = container;
									 break;
								 }
							 }
						 }
					 } else if (position == "21") { //pod pierwszym zdjęciem za składnikami
						 var offsetTop = 0;
						 var span = null;
						 if (lastNode.nodeType != 1) {
							 span = document.createElement("span");
							 EkcalInsertAfter(span, lastNode);
							 offsetTop = span.offsetTop;
						 } else {
							 offsetTop = lastNode.offsetTop;
						 }
						 for (var i = 0; i < imgsArr.length; i++) {
							 var container = EkcalGetAncestorChild(imgs[i], lastNode.parentNode);
							 if (container != null && container.offsetTop > offsetTop) {
								 if (nextGroupNode == null || (nextGroupNode.parentNode == container.parentNode && nextGroupNode.offsetTop > container.offsetTop)) {
									 imgContainer = container;
									 break;
								 } else {
									 //pierwsze zdjęcie poniżej laży za następną grupą
									 break;
								 }
							 }
						 }
						 if (span != null) {
							 span.parentNode.removeChild(span);
						 }
					 }
				 }
			 }
			 if (imgContainer == null) {
				 position = "10";
			 } else {
				 EkcalInsertAfter(frame, imgContainer);
			 }
		}
		if (position == "10") {
			EkcalInsertAfter(frame, nodes[nodes.length - 1]);
		}
	}
}

function EkcalGetNodesBetween(nodeFrom, nodeTo) {
	if (nodeFrom.parentNode != nodeTo.parentNode) {
		return null;
	}
	var nodes = new Array();
	var children = nodeFrom.parentNode.childNodes;
	var started = false;
	for (var i = 0; i < children.length; i++) {
		var child = children[i];
		if (started == false && child == nodeFrom) {
			started = true;
		}
		if (started) {
			nodes.push(child);
			if (child == nodeTo) {
				break;
			}
		}
	}
	return nodes;
}

function EkcalHasImageWithin(node) {
	if (node.nodeType == 1) {
		if (node.nodeName.toLowerCase() == "img") {
			return true;
		}
		var children = node.childNodes;
		for (var i = 0; i < children.length; i++) {
			if (EkcalHasImageWithin(children[i])) {
				return true;
			}
		}
		return false;
	} else {
		return false;
	}
}

function EkcalGetAncestor(node, ancestorClass) {
	var ancestorClassPattern = arguments[2];
	if (ancestorClassPattern == null) {
		ancestorClassPattern = new RegExp("(^|\\s)" + ancestorClass + "(\\s|$)");
	}
	var parent = node.parentNode;
	if (parent == null) {
		return null;
	}
	if (ancestorClassPattern.test(parent.className)) {
		return parent;
	}
	return EkcalGetAncestor(parent, ancestorClass, ancestorClassPattern);
}

/*Zwraca przodka node będącego dzieckiem ancestor node*/
function EkcalGetAncestorChild(node, ancestorNode) {
	var parent = node.parentNode;
	if (parent == ancestorNode) {
		return node;
	}
	return parent == null || parent.nodeType != 1 ? null : EkcalGetAncestorChild(parent, ancestorNode);
}

function EkcalInsertAfter(newElement,targetElement) {
	var parent = targetElement.parentNode;
	if(parent.lastchild == targetElement) {
		parent.appendChild(newElement);
	} else {
		parent.insertBefore(newElement, targetElement.nextSibling);
	}
}

function EkcalGetFirstElementText(root, tagName, defValue) {
	var childNodes = root.childNodes;
	for (var i = 0; i < childNodes.length;i++) {
		var child = childNodes[i];
		if (child.nodeType == 1) {
			if (child.nodeName == tagName) {
				var val = child.firstChild.textContent;
				return val != null ? val : child.firstChild.text;
			} else {
				var val = EkcalGetFirstElementText(child, tagName, null);
				if (val != null) {
					return val;
				}
			}
		}
	}
	return defValue;
}

function EkcalXMLWriter(initialContent) {
	
	this.Content = initialContent;
	
	this.append = function(content) {
		this.Content += content;
	};
	
	this.openTag = function(tagName) {
		this.append("<"+tagName+">");
	};
	
	this.closeTag = function(tagName) {
		this.append("</"+tagName+">");
	};
		
	this.appendTag = function(tagName, content) {
		this.openTag(tagName);
		this.append(content);
		this.closeTag(tagName);
	};
	
	this.getXML = function() {
		return this.Content;
	};
}

function EkcalConsole(s) {
	//console.log(s);
}


/**
 * module used to process the blog content - used when DOM structure is not sufficient to recognize recipie's ingredients
 * @param {}
 */
function EkcalBlogContentsProcessor(blogentry) {
	// 	Congiguration section

	/**
	 * regex used to find Ingredients List Header
	 */
	this._IngredientsRegex = /(składnik|produkty|ingredient)/ig;
	/*
	 * reges used to find Intstrctions List/Description Header - indication often end of ingredients list
	 */
	this._InstructionsRegex = /(wykonanie|instruk|przygotowani|instruction|direction|preparation)/ig;
	/**
	 * regex used to recognize bullet pattern in the ingredient lists
	 * modify when need to change this behaviour
	 */
	this._BulletRegex = /^\s*(-|\+|â€˘|Â·)\s*/ig;

	/**
	 * max distance in lines between two bulleted items used for grouping purposes
	 * allows section titles in between bulleted items
	 * prevents from grouping bulleted items separated by a large paragraph of text.
	 */
	this.IngredientBulletMaxDistance = 3;

	/**
	 * max word count for a section title between
	 */
	this.SectionNameMaxWordCount = 6;
	//wc = word count

	//	Processing Section
	this.BlogEntryOriginal = blogentry;

	this.BlogEntry = blogentry;

	this.HasIngridientsHeader = false;

	this.EntryText = "";

	this.SplittedEntryText = null;
	this.SplittedEvaluation = new Array();

	this.Lists = new Array();
	
	this.processText = function() {

		//najpierw operujemy na oryginalnym elemnecie z drzewa DOM
		this.BlogEntry = this.BlogEntryOriginal;

		//new line characters - /n - are inserted for better text format recogniton
		// - eg. putting <br /> doesn't generate new line character in all browsers
		//new line characters need to be inserted in the original DOM tree otherwise it is not visible for IE
		//white characters we add does not affect the way particular HTML is rendered
		this.getEntryText();

		this.BlogEntry = this.BlogEntryOriginal.cloneNode(true);

		this.HasIngridientsHeader = this.hasIngredients();

		if(this.HasIngridientsHeader) {
			//dzieli tekst na linie
			this.splitEntryText();

			//usuwa zbędne puste linie
			this.clearSplittedText();

			//wykrywa gdzie zaczynają się skłaniki (tekst zawierający słowo składniki lub podobne)
			// mają one ustawiony znacznik 'hasIngredients' w SplittedEvaluation
			this.evalIngredients();

			//szuka wypunktowań, ustawia im znacznik 'hasBullet' w SplittedEvaluation 
			this.evalBullet();

			//wykrywa listy składników podzielonych na sekcje
			this.discoverLists();

		}

	};

	this.discoverLists = function() {
		
		var firstItemIndex = -1;	
		var lastItemIndex = -1;	
		var inGroup = false;
		var breakSections = 0;
		var lastBreakIndex = -1;
		var firstBreakIndex = -1;
		var endGroup = false;
		var endGroupByNextGroup = false;
		var groupItems = new Array();
		
		for(var i = 0; i < this.SplittedEntryText.length; i++) {
			
			if(this.SplittedEvaluation[i].hasIngredients == true && this.SplittedEvaluation[i].hasBullet == false) {
				if (inGroup) {
					// zakończ starą grupe, rozpocznij nową
					endGroupByNextGroup = true;
				} else {
					inGroup = true;
				}
				this.SplittedEvaluation[i].groupStart=true;
			} else {
				if (this.SplittedEvaluation[i].hasBullet) {
					this.SplittedEvaluation[i].listElement = true;
					groupItems.push(this.clearItem(this.SplittedEntryText[i]));
					if (firstItemIndex < 0) {
						firstItemIndex = i;
					}
					lastItemIndex = i;
					lastBreakIndex = -1;
					breakSections = 0;
				} else {
					// nie ma bullet, jeżeli w grupie sprawdź czy ok
					if (inGroup) {
						var text = this.SplittedEntryText[i];
						var trim = EkcalTrim(text);
						if (trim != null && trim.length > 0) {
							var validSection = this.isValidSectionName(text);
							if (validSection) {
								breakSections++;
							}
							if (breakSections > 2 || validSection == false) {
								endGroup = true;
							} else {
								if (firstBreakIndex < 0) {
									firstBreakIndex = groupItems.length;
								}
								//valid break section
								if (lastBreakIndex < 0) {
									lastBreakIndex = groupItems.length;
								}
								groupItems.push(this.clearItem(this.SplittedEntryText[i]));
							}
						}
					}
				}				
			}
			if (i == this.SplittedEntryText.length - 1 && inGroup == true) {
				endGroup = true;
			}
			if (endGroup || endGroupByNextGroup) {
				if (lastItemIndex >= 0) {
					this.SplittedEvaluation[i].lastElement = true;
				}				
				if (!endGroupByNextGroup) {
					inGroup = false;
				}
				//usuń sekcje-breaki dodane na koniec listy
				if (lastBreakIndex >= 0) {
					groupItems.splice(lastBreakIndex, groupItems.length - lastBreakIndex);
				}
				//sprawdz czy grupa zawiera wewnątrz sekcje - jeżeli tak to dodaj pierwsze zdanie do items
				if (firstBreakIndex >= 0 && (lastBreakIndex < 0 || firstBreakIndex < lastBreakIndex)) {
					 var firstLineBefore = this.getFirstLineBeforeLine(this.SplittedEntryText, firstItemIndex);
					 if (firstLineBefore != null) {
						 groupItems.splice(0,0, firstLineBefore);
					 }
				}
				if (firstItemIndex > 0 && lastItemIndex >= firstItemIndex) {
					var nodesBetween = this.getNodesBetween( this.BlogEntryOriginal, firstItemIndex, lastItemIndex, false);
					if (nodesBetween != null) {					
						var List = new Object();
						List.nodes = nodesBetween;
						List.items = groupItems;
						this.Lists.push(List);
					}
				}
				// szukaj nowej grupy
				groupItems = new Array();
				firstItemIndex = -1;
				lastItemIndex = -1;
				breakSections = 0;
				lastBreakIndex = -1;
				firstBreakIndex = -1;
				endGroup = false;
				endGroupByNextGroup = false;
			}
		}
	};
	
	this.getFirstLineBeforeLine = function(lines, lineIndex) {
		for (var i = lineIndex - 1; i >= 0; i--) {
			var line = lines[i];
			var trim = EkcalTrim(line);
			if (trim != null && trim.length > 0) {
				return trim;
			}
		}
		return null;
	};
	
	this.getTextCount = function(indexFrom, indexTo) {
		var count = 0;
		var text = this.SplittedEntryText[indexTo];
		for (var i = indexFrom; i <= indexTo; i++) {
			if (this.matchesText(this.SplittedEntryText[i], text)) {
				count++;
			}
		}
		return count;
	};

	this.matchesText = function(content, text) {
		return content.length == text.length && content.indexOf(text) == 0;
	}

	this.getNodesBetween = function(rootNode, indexFrom, indexTo, isBetween) {
		if (rootNode == this.BlogEntryOriginal) {
			this.NodesBetween = new Array();
			this.NodesBetweenFound = false;
			this.FirstTextCount = this.getTextCount(0 , indexFrom); // ilość tekstów takich jak pierwszy tekst
			this.FirstTextNodeIndex = 0; // ilość znalezionych nodów taki jak pierwszy tekst
			this.LastTextCount = this.getTextCount(indexFrom, indexTo); // ilość tekstów takich jak ostatni tekst
			this.LastTextNodeIndex = 0;// ilość znalezionych nodów taki jak ostatni tekst
		} else if (this.NodesBetweenFound == true) {
			return null;
		}
		// szukaj pierwszego lub ostatniego nodsa
		var seekText = !isBetween ? this.SplittedEntryText[indexFrom] : this.SplittedEntryText[indexTo];
		var childNodes = rootNode.childNodes;
		for (var i = 0; i < childNodes.length; i++) {
			if (this.NodesBetweenFound == true) {
				break;
			}
			var childNode = childNodes[i];
			if (isBetween) {
				this.NodesBetween.push(childNode);
			}
			// text wewnątrz może mieć znaki \n, więc przynajmniej 1 linia musi się zgadzać ze SplittedEntryText
			var splittedTexts = this.parseHTMLForText(childNode).split("\n")
			for (var j = 0; j < splittedTexts.length; j++) {
				if (this.matchesText(EkcalTrim(splittedTexts[j]), seekText)) {
					if (isBetween == false) {
						this.FirstTextNodeIndex++;
						if (this.FirstTextNodeIndex == this.FirstTextCount) {
							this.NodesBetween.push(childNode);
							isBetween = true;
							seekText = this.SplittedEntryText[indexTo];
						}
					} else {
						this.LastTextNodeIndex++;
						if (this.LastTextNodeIndex == this.LastTextCount) {
							this.NodesBetweenFound = true;
							break;
						}
					}
				}
			}
			this.getNodesBetween(childNode, indexFrom, indexTo, isBetween);
		}
		if (rootNode == this.BlogEntryOriginal && this.NodesBetweenFound == true) {
			return this.NodesBetween;
		}
		return null;
	};
	
	/**
	 * Grupuje UL'e w listy/grupy UL'i. Dla każdej grupy będzie widżet. Kluczowa jest metoda isValidSectionNameMultiline(),
	 * która określa czy przerwa między UL'ami (ilość słów - SectionNameMaxWordCount) jest na tyle mała że można je zgrupować
	 */
	this.processUlsGrouping = function(uls) {
		//console.log("processUlsGrouping:");
		
		var entryClone = null;
		var UlsEvaluation = new Array();
		var onErrorRollback = false;
		var clonedUls = new Array();

		if(uls && uls.length > 1) {

			//dodaje znaczniki UUID do HTMLa jako css class do originalnych UL'i oraz jako teksty do clone HTMLa, 
			//aby później po nich identyfikować grupy - parsując tekst
			for(var i = 0; i < uls.length; i++) {
				//add classes
				UlsEvaluation[i] = new Object();
				UlsEvaluation[i].UUID = this.randomUniqueString(this.BlogEntry.innerHTML);
				if(UlsEvaluation[i].UUID == null) {
					onErrorRollback = true;
					break;
				}
				uls[i].className += " " + UlsEvaluation[i].UUID;
			}

			//clone
			if(!onErrorRollback) {
				entryClone = this.BlogEntry.cloneNode(true);
			}

			for(var i = 0; i < uls.length; i++) {
				//remove classes
				if(UlsEvaluation[i].UUID) {
					var regexp = new RegExp("\s*" + UlsEvaluation[i].UUID + "$", "ig");
					uls[i].className = uls[i].className.replace(regexp, "");
					//console.log(uls[i]);
				}

				if(!onErrorRollback) {
					//pobiera UL'e z klona oraz wstawia znaczniki UUID do klona HTML
					var elements = this.getElementsByClassNameL(UlsEvaluation[i].UUID, entryClone, "ul");
					
					if(elements && elements.length > 0) {
						clonedUls[i] = elements[0];
						//add texts - pre and post
						this.insertAfter(clonedUls[i], document.createTextNode(UlsEvaluation[i].UUID));
						this.insertBefore(clonedUls[i], document.createTextNode(UlsEvaluation[i].UUID));
					}
				}
			}

			if(onErrorRollback) {
				return null;
			}

			//inner text
			entryClone.innerHTML = this.preprocessHTML(entryClone.innerHTML);
			var EntryText = this.parseHTMLForText(entryClone);
			//console.log("entry text:"+EntryText);
			//eval groups
			
			var textsBefore = new Array();
			for(var i = 0; i < clonedUls.length - 1; i++) {
				//get text between	ul[i] and ul[i+1]
				//console.log(EntryText);
				
				var firstIndex = this.getFirstInstanceIndex(EntryText, UlsEvaluation[i + 1].UUID);
				var lastIndex = this.getLastInstanceIndex(EntryText, UlsEvaluation[i].UUID);

				if(firstIndex > 0 && lastIndex > 0) {
					var substring = EntryText.substring(firstIndex, lastIndex);
					
					//console.log(substring);
					var hasValidSectionName = this.isValidSectionNameMultiline(substring);
					if(hasValidSectionName) {
						//sprawdź czy [i] należy do grupy
						if(UlsEvaluation[i].groupingFirstElement || UlsEvaluation[i].groupingElement) {

						} else {
							UlsEvaluation[i].groupingFirstElement = true;
							UlsEvaluation[i].groupingElement = true;
							var textBefore = this.getFirstLineBeforeIndex(EntryText, this.getFirstInstanceIndex(EntryText, UlsEvaluation[i].UUID));
							textsBefore.push(textBefore);
						}
						//nie to oznacz go jako początek grupy a [i+1] jako element grupy
						//tak to oznacz [i+1] jako element grupy
						UlsEvaluation[i + 1].groupingElement = true;
						textsBefore.push(EkcalRemoveEmptyLines(substring));
						//jeśli ostatni element listy oznacz jako ostatni element grupy
						if(i + 1 == clonedUls.length - 1) {
							UlsEvaluation[i + 1].groupingLastElement = true;
						}

					} else {
						//jeśli [i] jest wewnątrz grupy oznacz go jako ostatni z grupy
						if(UlsEvaluation[i].groupingElement) {
							UlsEvaluation[i].groupingLastElement = true;
						}
					}

				} else {
					if(UlsEvaluation[i].groupingElement) {
						UlsEvaluation[i].groupingLastElement = true;
					}
					continue;
				}
			}

		}
		return this.ulsToGroupedForm(uls, UlsEvaluation, textsBefore);
	};
	
	this.getFirstLineBeforeIndex = function(text, index) {
		var buff = null;
		for (var i = index - 1; i >= 0; i--) {
			var char = text.charAt(i);
			if (char == '\n' || char == '\r') {
				if (buff != null) {
					break;
				}
			} else if (buff != null) {
				buff += char;
			} else if (char != ' ' && char != '\t') {
				buff = char;
			}
		}
		return buff == null ? "" : EkcalTrim(EkcalReverse(buff));
	};
	
	
	this.ulsToGroupedForm = function(uls, UlsEvaluation, pTextsBefore) {
		var groups = new Array();		
		var textsBefore = new Array();
		var iterator = 0;
		var inGroup = false;
		for(var i = 0; i < uls.length; i++) {
			if(UlsEvaluation[i] && UlsEvaluation[i].groupingElement) {
				if(UlsEvaluation[i].groupingFirstElement) {
					groups[iterator] = new Array();
					groups[iterator].push(uls[i]);
					textsBefore[iterator] = new Array();
					textsBefore[iterator].push(pTextsBefore[i]);
					inGroup = true;
				} else if(UlsEvaluation[i].groupingLastElement) {
					groups[iterator].push(uls[i]);
					textsBefore[iterator].push(pTextsBefore[i]);
					iterator++;
					inGroup = false;
				} else {
					groups[iterator].push(uls[i]);
					textsBefore[iterator].push(pTextsBefore[i]);
				}
			} else {
				groups[iterator] = new Array();
				groups[iterator].push(uls[i]);
				textsBefore[iterator] = new Array();
				textsBefore[iterator].push("");
				iterator++;
			}

		}
		var result = new Object();
		result.groups = groups;
		result.textsBefore = textsBefore;
		//console.log(textsBefore);
		return result;
	};

	this.isValidSectionNameMultiline = function(str) {
		var SplittedText = str.split("\n");
		//console.log(SplittedText);
		SplittedText = this._clearSplittedText(SplittedText);
		//console.log(SplittedText);

		var sectionNameDiscovered = false;

		for(var i = 0; i < SplittedText.length; i++) {
			if(SplittedText[i] && SplittedText[i].length > 0) {
				if(this.isValidSectionName(SplittedText[i])) {
					if(!sectionNameDiscovered) {
						sectionNameDiscovered = true;
					} else {
						return false;
					}
				} else {
					return false;
				}
			}
		}
		return true;
	};
	
	this.isValidSectionName = function(str) {

		if(str && str.length > 0) {
			//nie zawiera w sobie wykonanie
			var match = str.match(this._InstructionsRegex);

			if(match == null || match.length == 0) {
				//sprawdź czy nie przekracza max wc
				if(EkcalWordCount(str) <= this.SectionNameMaxWordCount) {
					return true;
				}
			}
		}
		return false;
	};

	this.getFirstInstanceIndex = function(str, query) {
		var reg = new RegExp(query, "g");
		var match = reg.exec(str);
		if(match != null) {
			return match.index;
		}
		return -1;
	};

	this.getLastInstanceIndex = function(str, query) {
		var reg = new RegExp(query, "g");
		var match = reg.exec(str);
		var result = -1;
		while(match != null) {
			result = match.index + query.length;
			match = reg.exec(str);
		}

		return result;
	};

	this.getEntryText = function() {
		//TODO: fix so it doesn't affect live HTML
		this.BlogEntry.innerHTML = this.preprocessHTML(this.BlogEntry.innerHTML);
		/*this.BlogEntry =*/
		this.preprocessDIVs(this.BlogEntry);
		this.EntryText = this.parseHTMLForText(this.BlogEntry);
		return this.EntryText;
	};

	this.preprocessHTML = function(innerhtml) {
		var result = innerhtml.replace(/<br\s*\/?>(?!=\\n)/ig, "<br \/>\n");
		return result;
	};
	//TODO: zweryfikować czy potrzebne
	this.preprocessDIVs = function(elem) {
		var elements = elem.getElementsByTagName('div');
		var textNode = document.createTextNode("\n");
		
		for(var i = 0; i < elements.length; i++) {
			//
			try{
			if(elements[i] && (!elements[i].style.display || elemenst[i].style.display == 'block') && (!elements[i].style.cssFloat || elements[i].style.cssFloat == 'none')) {
				this.insertAfter(elements[i], textNode.cloneNode(true));
			}
			}catch(err){}
		}
		
	};

	/**
	 * Dzieli tekst na linie
	 */
	this.splitEntryText = function() {
		this.SplittedEntryText = this.EntryText.split("\n");
		return this.SplittedEntryText;
	};

	this.clearSplittedText = function() {
		var result = this._clearSplittedText(this.SplittedEntryText);
		return result;
	};

	this._clearSplittedText = function(strArray) {
		var result = new Array();
		for(var i = 0; i < strArray.length; i++) {
			var buf = EkcalTrim(strArray[i]);
			//if (buf && buf.length>=0){
			result.push(buf);
			//}
		}
		this.SplittedEntryText = result;
		return result;
	};

	this.hasIngredients = function() {
		var hasmatch = (this.EntryText).match(this._IngredientsRegex);

		if(hasmatch != null) {
			return true;
		} else {
			return false;
		}
	};

	this.evalRegex = function(regex, property) {
		for(var i = 0; i < this.SplittedEntryText.length; i++) {
			var hasmatch = this.SplittedEntryText[i].match(regex);

			if(this.SplittedEvaluation[i] == undefined) {
				this.SplittedEvaluation[i] = new Object();
			}

			if(hasmatch != null) {
				//console.log(property);
				//console.log(this.SplittedEntryText[i]);
				this.SplittedEvaluation[i][property] = true;
			} else {
				this.SplittedEvaluation[i][property] = false;
			}
		}
	};

	/**
	 * sprawdza czy linia zaczyna się od wypunktowania
	 */
	this.evalBullet = function() {
		this.evalRegex(this._BulletRegex, "hasBullet");
	};


	this.evalIngredients = function() {
		this.evalRegex(this._IngredientsRegex, "hasIngredients");
	};

	this.clearItem = function(str) {
		//str = str.replace(this._BulletRegex, '');
		//str = str.replace(/\s*,$/ig, '');
		str = this.escapeApostrophe(str);

		return str;
	};

	this.escapeApostrophe = function(str) {
		var result = str.replace(/\'/gi, "\\'");
		return result;
	};

	this.hasInnerText = (document.getElementsByTagName("body")[0].innerText != undefined) ? true : false;
	/**
	 *
	 * @param {Object} element
	 */
	this.parseHTMLForText = function(element) {
		var result = "";
		if(element) {
			if(element.nodeType == 1) {
				if(this.hasInnerText) {
					result = element.innerText;
				} else {
					result = element.textContent;
				}
			} else if(element.nodeType == 3) {
				result = element.nodeValue;
			}
		}
		return result;
	};

	this.randomUniqueString = function(str) {
		for(var i = 0; i < 1000; i++) {
			var rndString = EkcalRandomString();
			var index = str.indexOf(rndString);
			if(index < 0) {
				return rndString;
			}
		}
		return null;
	};

	this.getElementsByClassNameL = function(searchClass, node, tag) {
		var classElements = new Array();
		if(node == null)
			node = document;
		if(tag == null)
			tag = '*';

		var els = node.getElementsByTagName(tag);
		var elsLen = els.length;
		var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
		for( i = 0, j = 0; i < elsLen; i++) {
			if(pattern.test(els[i].className)) {
				classElements[j] = els[i];
				j++;
			}
		}
		return classElements;
	};

	this.insertAfter = function(currentElement, newElement) {

		var nextObj = currentElement.nextSibling;
		if(nextObj) {
			//console.log("first");
			currentElement.parentNode.insertBefore(newElement, nextObj);
		} else {
			//console.log("second");
			currentElement.parentNode.appendChild(newElement);
		}

	};

	this.insertBefore = function(currentElement, newElement) {
		//console.log("third");
		currentElement.parentNode.insertBefore(newElement, currentElement);
	};

	this.isdefined = function(variable) {
		return ( typeof (window[variable]) == "undefined") ? false : true;
	};

}

/**
 * BlogProcessor - used to process blog entres searching for specific DOM tree patterns indicating list of ingredient.
 */
function BlogProcessor(type) {
	this.type = type;

	this.isIndividualView = false;
	//określa czy jesteśmy w widoku pojedynczego wpisu czy całościowym
	//ma znaczenie przy blox.pl

	this.widgetInfoUrl = "http://listonic.pl/c/cms/blogwidget_about";

	this.buttonsInfoUrl = "http://na1000sposobow.pl/jak-to-dziala/";
	
	
	this.language = typeof listonic_language=="string"?listonic_language:"pl_PL";
	this.language = this.language.toLowerCase();
		
	//defines language specific resource eg. texts, images, urls, parameters steering other mechanisms 
	this.resources = {
		pl_PL : {
			themePrefix : "",
			themeSuffix : "",
			howItWorksText : "Jak to działa?",
			howItWorksUrl : "http://na1000sposobow.pl/jak-to-dziala/",
			iWantButtonText : "Chcę taki przycisk",
			iWantButtonUrl : "http://listonic.pl/c/cms/blogwidget_about"
		},
		en_US : {
			themePrefix : "",
			themeSuffix : "_en",
			howItWorksText : "How it works?",
			howItWorksUrl : "http://na1000sposobow.pl/jak-to-dziala/",
			iWantButtonText : "I want button.",
			iWantButtonUrl : "http://listonic.pl/c/cms/blogwidget_about"
			
		}
	}; 
	
	this.ekcalService = "ekcal.com";
	
	this.buttonUrl = "http://"+this.ekcalService+"/bridge?actionId=check";
	this.ekcalWidgetSrcSubString = this.ekcalService+"/embed";
	
	this.listonic_iframeclass = "listonic_ifrm";
	this.listonic_iframeusedclass = "listonic_ifrm_used";

	this.minCount = 3;
	this.minCountDefined = true;

	this.iframeWidth = null;
	this.iframeWidthDefined = false;

	this.sidebarVisible = false;

	this.genericVars = new Array();
	
	/**
	 * functions
	 */
	this.getBlogEntries = null;
	this.checkIfInclude = null;
	this.getEntryContent = null;
	this.getEntryTitle = null;
	this.getEntryUrl = null;

	this.getEntryUrl = null;

	this.getResource = function() {
		var resource = this.resources.pl_PL;
	
		if (this.language.substr(0,3) == "en_" || this.language == "en"){
			resource = this.resources.en_US;
		}
		
		return resource;
	};

	//TODO: verify if requried
	this.generateSidebarContentNoIframe = function() {
		var resource = this.getResource();

		var head = document.getElementsByTagName("head");
		if(head && head.length > 0) {

			var link = document.createElement("link");
			link.rel = "stylesheet";
			link.type = "text/css";
			link.href = "http://static.listonic.com/content/buttonPanel/basic"+resource.themeSuffix +".css";

			head[0].appendChild(link);

			document.write('<table class="listonicBox">' + '<tr>' + '<td class="boxL"></td>' + '<td class="boxM">' + '<div class="mainTxt"></div>' + '<div class="buttons">' + '<a href="' + this.resource.howItWorksUrl + '" class="howItWorks">' + resource.iWantButtonText +'</a>' + '<a href="' + resource.iWantButtonUrl + '" class="' + resource.iWantButtonText + '">' + 'Chcę taki przycisk.' + '</a>' + '</div>' + '</td>' + '<td class="boxR"></td>' + '</tr>' + '</table>');
		}

	};

	this.generateSidebarContent = function() {
		var resource = this.getResource();
		
		document.write("<iframe id='listonic_blog_widget' frameborder='0' scrolling='no'> </iframe>");

		var iframe = document.getElementById("listonic_blog_widget");

		var height = 150;
		var width = 175;
		var bgcolor = "#ffffff";
		var theme = "addtoshoppinglist1";

		if(iframe) {
			
			if(this.iframeWidthDefined) {
				width = this.iframeWidth;
			}

			iframe.height = height;
			iframe.width = width;

			var iframeDoc;
			if(iframe.contentDocument) {
				iframeDoc = iframe.contentDocument;
			} else if(iframe.contentWindow) {
				iframeDoc = iframe.contentWindow.document;
			} else if(window.frames[iframe.name]) {
				iframeDoc = window.frames[iframe.name].document;
			}

			if(iframeDoc) {
				iframeDoc.write('<html><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"><title>Listonic</title><style>body { margin: 0; padding: 0}</style><link rel="stylesheet" type="text/css" href="http://static.listonic.com/content/buttonPanel/basic' + resource.themeSuffix + '.css" />' + '</head><body>' + '<table class="listonicBox">' + '<tr>' + '<td class="boxL"></td>' + '<td class="boxM">' + '<div class="mainTxt"></div>' + '<div class="buttons">' + '<a href="' + resource.howItWorksUrl + '" onclick="javascript: window.parent.location = \'' + resource.howItWorksUrl + '\';" class="howItWorks">' + resource.howItWorksText + '</a>' + '<a href="' + resource.iWantButtonUrl + '" onclick="javascript: window.parent.location = \'' + resource.iWantButtonUrl + '\';" class="iWantButton">'+resource.iWantButtonText+'</a>' + '</div>' + '</td>' + '<td class="boxR"></td>' + '</tr>' + '</table>');

				iframeDoc.write('<\/body><\/html>');

				iframeDoc.close();

			}
		}

	};

	this.Init = function() {
		this.initType();

		if(this.isdefined('ekcal_minCount')) {
			this.minCount = parseInt(ekcal_minCount);
			this.minCountDefined = true;
		}
		
		if(this.isdefined('ekcal_iframeWidth')) {

			this.iframeWidth = parseInt(ekcal_iframeWidth);
			if(this.iframeWidth && this.iframeWidth > 0) {
				this.iframeWidthDefined = true;
			}
		}
		
		if(this.isdefined('ekcal_hideSideBar')) {
			this.sidebarVisible = !Boolean(ekcal_hideSideBar);
		}

		var do_turnOff = false;
		if(this.isdefined('ekcal_turnOff')) {
			do_turnOff = Boolean(ekcal_turnOff);
		}

		if(!do_turnOff) {
			if(this.type) {
				if(this.type == "blox") {
					this.initBlox();
				} else if(this.type == "bloog") {
					this.initBloog();
				} else if(this.type == "blogger") {
					this.initBlogger();
				} else if(this.type == "generic") {
					this.initGeneric();
				}

			}
		}

		if(this.sidebarVisible) {
			this.generateSidebarContent();
		}

	};

	this.initType = function() {
		if(this.isdefined('ekcal_type')) {
			this.type = ekcal_type;
			return;
			//TODO: make it more stupid proof
			//validate choice
		}

		var domain = window.location.hostname;

		if(domain.match(/.*blogspot.com$/ig)) {
			this.type = "blogger"
		} else if(domain.match(/.*blox.pl$/ig)) {
			this.type = "blox";
		} else if(domain.match(/.*bloog.pl$/ig)) {
			this.type = "bloog";
		}
	};

	this.initBlox = function() {
		this.getBlogEntries = this.getBlogEntriesBlox;
		this.checkIfInclude = this.checkIfIncludeBlox;
		this.getEntryContent = this.getEntryContentBlox;
		this.getEntryTitle = this.getEntryTitleBlox;
		this.getEntryUrl = this.getEntryUrlBlox;
	};

	this.initBloog = function() {
		this.getBlogEntries = this.getBlogEntriesBloog;
		this.checkIfInclude = this.checkIfIncludeBloog;
		this.getEntryContent = this.getEntryContentBloog;
		this.getEntryTitle = this.getEntryTitleBloog;
		this.getEntryUrl = this.getEntryUrlBloog;
	};

	this.initBlogger = function() {
		this.getBlogEntries = this.getBlogEntriesBlogger;
		this.checkIfInclude = this.checkIfIncludeBlogger;
		this.getEntryContent = this.getEntryContentBlogger;
		this.getEntryTitle = this.getEntryTitleBlogger;
		this.getEntryUrl = this.getEntryUrlBlogger;
	};

	this.initGeneric = function() {
		//init content id 
		this.initGenericVar('ekcal_generic_contentId', 'contentId');
		//or content class/tag pair
		this.initGenericVar('ekcal_generic_contentClass', 'contentClass');
 		this.initGenericVar('ekcal_generic_contentTag', 'contentTag');

		this.initGenericVar("ekcal_generic_contentAsEntry", 'contentAsEntry');

		this.initGenericVar('ekcal_generic_entryClass', 'entryClass');
		this.initGenericVar('ekcal_generic_entryTag', 'entryTag');

		this.getBlogEntries = this.getBlogEntriesGeneric;

		this.checkIfInclude = this.checkIfIncludeGeneric;

		this.initGenericVar('ekcal_generic_entryContentClass', 'entryContentClass');
		this.initGenericVar('ekcal_generic_entryContentTag', 'entryContentTag');
		this.getEntryContent = this.getEntryContentGeneric;

		this.initGenericVar('ekcal_generic_entryHeaderTitleClass', 'entryHeaderTitleClass');
		this.initGenericVar('ekcal_generic_entryHeaderTitleTag', 'entryHeaderTitleTag');
		this.getEntryTitle = this.getEntryTitleGeneric;

		this.initGenericVar('ekcal_generic_entryHeaderUrlWrapperClass', 'entryHeaderUrlWrapperClass');
		this.initGenericVar('ekcal_generic_entryHeaderUrlWrapperTag', 'entryHeaderUrlWrapperTag');
		this.initGenericVar('ekcal_generic_entryHeaderUrlClass', 'entryHeaderUrlClass');
		this.getEntryUrl = this.getEntryUrlGeneric;

	};

	this.initGenericVar = function(generic_var, saveas) {
		if(this.isdefined(generic_var)) {
			this.genericVars[saveas] = eval(generic_var);

		}
	};

	this.process = function() {
		EkcalConsole("Start process...");
		if(this.getBlogEntries) {

			var entries = this.getBlogEntries();

			this.processBlogEntries(entries);
		}
		EkcalConsole("End process.");
	};

	this.getBlogEntriesBlogger = function() {
		var contentDiv = document.getElementById("Blog1");

		var entries = this.getElementsByClassNameL("post", contentDiv, "div");

		return entries;
	};

	this.getBlogEntriesBloog = function() {

		var contentDiv = document.getElementById("tresc");

		var entries = this.getElementsByClassNameL("wpis", contentDiv, "div");

		//TODO: verify id format

		return entries;
	};

	this.getBlogEntriesBlox = function() {

		var entries = this.getElementsByClassNameL("BlogWpisBox", null, "div");

		if(entries.length <= 0) {
			entries[0] = document.getElementById("SkomentujBox");
			if(entries[0]) {
				this.isIndividualView = true;
			}
		} else {
			this.isIndividualView = false;
		}

		return entries;
	};

	this.getBlogEntriesGeneric = function() {
		var contentCol;
		if(this.IsSet(this.genericVars['contentId'])) {
			contentCol = document.getElementById(this.genericVars['contentId']);
		}else if (this.IsSet(this.genericVars['contentClass'])){
			var elements = this.getElementsByClassNameL(this.genericVars['contentClass'], null, this.genericVars['contentTag']);
			
			if(elements && elements.length > 0) {
				result = elements[0].href;
			}			
			
		}

		var entries = null;

		if(contentCol) {
			if(this.genericVars['contentAsEntry']) {
				entries = new Array();

				entries[0] = contentCol;
				return entries;
			}
		} else {
			contentCol = null;
		}
		entries = this.getElementsByClassNameL(this.genericVars['entryClass'], contentCol, this.genericVars['entryTag']);

		return entries;
	};

	this.processBlogEntries = function(entries) {

		if(entries) {
			if(entries.length > 0) {
				//console.log(entries.length);

				for(var i = 0; i < entries.length; i++) {//TODO: fix
					//console.log(entries[i]);

					//check if suitable for processing
					if(this.checkIfInclude(entries[i])) {

						this.processBlogEntry(entries[i]);
					}

				}
			}
		}
	};

	this.processText = function(content) {
		var tp = new EkcalBlogContentsProcessor(content);

		tp.processText();

		if(tp.Lists && tp.Lists.length > 0) {
			return tp.Lists;
		}

	};

	this.processBlogEntry = function(entry) {
		//console.log(entry);

		var entrycontents = this.getEntryContent(entry);

		//console.log(entrycontents);

		if(entrycontents && entrycontents.length > 0) {

			var entrycontent = entrycontents[0];

			var entrytitle = this.getEntryTitle(entry)
			entrytitle = EkcalTrim(entrytitle);

			var entryurl = this.getEntryUrl(entry);

			var uls = entrycontent.getElementsByTagName("ul");

			//uls processing
			if(uls && uls.length > 0) {
				//console.log(uls);

				var tp = null;
				var groupedULS = null;

				//składniki rozbite na kilka sekcji
				//trzeba grupować
				if(uls.length >= 2) {
					tp = new EkcalBlogContentsProcessor(entrycontent);
					groupedULS = tp.processUlsGrouping(uls);
				} else {
					var groups = new Array();
					var textsBefore = new Array();
					groups[0] = new Array();
					groups[0].push(uls[0]);
					textsBefore[0] = new Array();
					textsBefore[0].push("");
					groupedULS = new Object();
					groupedULS.groups = groups;
					groupedULS.textsBefore = textsBefore;
				}
				var groups = groupedULS.groups;
				var len = groups.length;
				EkcalConsole("UL groups:"+groups.length);
				var prevWidgetId = null;
				for(var i = 0; i < len; i++) {
					var items = this.processULGroup(groupedULS, i);
					EkcalConsole(items);
					if(items != null) {
						prevWidgetId = this.generateWidget(groups[i], items, entrytitle, entryurl, i, prevWidgetId);
					} else {
						continue;
					}
				}
				//text processing
			} else {
				//console.log("Texts.Processing");
				var lists = this.processText(entrycontent);
				if(lists) {
					var prevWidgetId = null;
					for(var i = 0; i < lists.length; i++) {
						var list = lists[i];
						if(list.items && list.nodes) {
							if(this.hasMinCount(list.items)) {
								prevWidgetId = this.generateWidget(list.nodes, list.items, entrytitle, entryurl, i, prevWidgetId);
							}
						}
					}
				}
			}
		}
	};
	

	this.processULGroup = function(groupedULS, index) {
		var items = new Array();
		var groups = groupedULS.groups[index];
		var textsBefore = groupedULS.textsBefore[index];
		var ingredientCount = 0;
		for(var j = 0; j < groups.length; j++) {
			var ul = groups[j];
			
			var lis = ul.getElementsByTagName("li");
			if(lis && lis.length > 0) {
				// sprawdzić, czy jest wymagana minilana ilość?
				
				var textBefore = textsBefore[j];
				if (textBefore != null && textBefore.length > 0) {
					var splitBefore = textBefore.split('\n');
					if (splitBefore.length > 0) {
						for (var i = 0; i < splitBefore.length; i++) {
							var trimmed = EkcalTrim(splitBefore[i]);
							if(trimmed) {
								items.push(trimmed);
							}
						}
					}
				}
				
				for(var i = 0; i < lis.length; i++) {

					var buf = this.parseHTMLForEscapedText(lis[i]);

					var trimmed = EkcalTrim(buf);
					if(trimmed) {
						ingredientCount++;
						items.push(trimmed);
					}
				}
			}
			
		}
		
		return this.biEqMinCount(ingredientCount) ? items : null;

	};

	this.getEntryUrlGeneric = function(entry) {
		var result = null;

		if(entry) {

			//TODO : get header first if defined

			var wrapperClass = this.genericVars['entryHeaderTitleClass'];
			var wrapperTag = this.genericVars['entryHeaderTitleTag'];

			var titleElem = this.getElementsByClassNameL(wrapperClass, entry, wrapperTag);

			if(titleElem && titleElem.length > 0) {

				var urlClass = this.genericVars['entryHeaderUrlClass'];

				var alink = null;

				if(urlClass) {
					alink = this.getElementsByClassNameL(urlClass, titleElem[0], 'a');
				} else {
					alink = titleElem[0].getElementsByTagName("a");
				}

				if(alink && alink.length > 0) {
					result = alink[0].href;
				}
			}//else{ result = getEntryUrlFooterGeneric(entry);}

			//todo: get from footer if there is no title
			//dodać w razie zaistnienia potrzeby obsłużenia

		}

		return result;
	}

	this.getEntryUrlBlogger = function(entry) {
		var result = null;

		if(entry) {
			var titleElem = this.getElementsByClassNameL("post-title", entry, "h3");

			if(titleElem && titleElem.length > 0) {

				var alink = titleElem[0].getElementsByTagName("a");

				if(alink && alink.length > 0) {
					result = alink[0].href;
				}
			} else {// nie podano tytułu
				result = this.getEntryUrlFooterBlogger(entry);
			}

		}

		return result;
	};

	this.getEntryUrlFooterBlogger = function(entry) {
		var result = null;
		if(entry) {
			var footer = this.getElementsByClassNameL("post-footer", entry, "div");

			if(footer && footer.length > 0) {
				var timestamp = this.getElementsByClassNameL("post-timestamp", footer[footer.length - 1], "span");

				if(timestamp && timestamp.length > 0) {
					var alink = timestamp[0].getElementsByTagName("a");

					if(alink && alink.length > 0) {
						result = alink[0].href;
					}

				}
			}

		}
		return result;
	};

	this.getEntryUrlBloog = function(entry) {
		var result = null;

		if(entry) {
			var titleElem = entry.getElementsByTagName("h2");

			if(titleElem && titleElem.length > 0) {

				var alink = titleElem[0].getElementsByTagName("a");

				if(alink && alink.length > 0) {
					result = alink[0].href;
				}
			}
		}
		return result;
	};

	this.getEntryUrlBlox = function(entry) {
		var result = null;

		if(entry) {
			var footerElem = this.getElementsByClassNameL("BlogWpisInfo", entry, "div");

			if(footerElem && footerElem.length > 0) {

				var linkElem = this.getElementsByClassNameL("IPTlinkuj", footerElem[0], "a")

				if(linkElem && linkElem.length > 0) {
					result = linkElem[0].href;
				}

			}
		}

		return result;
	};

	this.getEntryTitleBlogger = function(entry) {
		var result = null;

		if(entry) {
			var titleElem = this.getElementsByClassNameL("post-title", entry, "h3");

			if(titleElem && titleElem.length > 0) {
				result = this.parseHTMLForEscapedText(titleElem[0]);
			}

		}

		return result;
	};

	this.getEntryTitleBloog = function(entry) {
		var result = null;

		if(entry) {
			var titleElem = entry.getElementsByTagName("h2");

			if(titleElem && titleElem.length > 0) {
				result = this.parseHTMLForEscapedText(titleElem[0]);
			}
		}

		return result;
	};

	this.getEntryTitleBlox = function(entry) {
		var result = null;
		if(entry) {

			var titleElemClass = "BlogWpisItemTytul";

			if(this.isIndividualView) {
				titleElemClass = "TytulKomentowanegoWpisu";
			}

			var titleElem = this.getElementsByClassNameL(titleElemClass, entry, "div");

			if(titleElem && titleElem.length > 0) {
				result = this.parseHTMLForEscapedText(titleElem[0]);
			}
		}
		return result;
	};

	this.getEntryTitleGeneric = function(entry) {
		var result = null;
		//console.log("getEntryTitleGeneric");
		if(entry) {
			var cssclass = this.genericVars['entryHeaderTitleClass'];
			var tag = this.genericVars['entryHeaderTitleTag'];

			var titleElem = this.getElementsByClassNameL(cssclass, entry, tag);

			if(titleElem && titleElem.length > 0) {
				result = this.parseHTMLForEscapedText(titleElem[0]);

			}
		}

		//console.log(result);
		return result;
	};

	this.getEntryContentBlogger = function(entry) {
		var result = null;
		if(entry) {
			result = this.getElementsByClassNameL("post-body", entry, "div");
		}

		return result;
	};

	this.getEntryContentBloog = function(entry) {
		var result = null;
		if(entry) {
			result = this.getElementsByClassNameL("txt", entry, "div");
		}
		return result;
	};

	this.getEntryContentBlox = function(entry) {
		var result = null;
		if(entry) {
			if(this.isIndividualView) {
				result = this.getElementsByClassNameL("TrescKomentowanegoWpisu", entry, "div");
			} else {
				result = this.getElementsByClassNameL("BlogWpisTresc", entry, "div");
			}
		}
		return result;
	};

	this.getEntryContentGeneric = function(entry) {
		var result = null;
		//console.log(entry);
		if(entry) {

			if(this.IsSet(this.genericVars['entryContentClass']) || this.IsSet(this.genericVars['entryContentTag'])) {
				result = this.getElementsByClassNameL(this.genericVars['entryContentClass'], entry, this.genericVars['entryContentTag']);
			} else {
				//nie ma żadnych informacji nt content zwracaj całe entry
				result = new Array();
				result[0] = entry;
			}
		}
		return result;
	};

	this.checkIfIncludeBlogger = function(entry) {
		var result = true;
		//sprawdź czy nie istnieje już listonic button
		if(this.hasEkcalWidget(entry)) {
			return false;
		}

		return result;
	};

	this.checkIfIncludeBloog = function(entry) {
		if(this.hasEkcalWidget(entry)) {
			return false;
		}

		return true;
	};

	this.checkIfIncludeBlox = function(entry) {
		var result = true;
		//sprawdź czy nie istnieje już listonic button
		if(this.hasEkcalWidget(entry)) {
			return false;
		}
		return result;
	};

	this.checkIfIncludeGeneric = function(entry) {
		if(this.hasEkcalWidget(entry)) {
			return false;
		}
		return true;
	};
	
	this.hasMinCount = function(items) {
		if(this.minCountDefined) {
			return items && items.length >= this.minCount && items.length > 0;
		} else {
			return items && items.length > 0;
		}
	};
	
	this.biEqMinCount = function(count) {
		if(this.minCountDefined) {
			return count >= this.minCount;
		} else {
			return count > 0;
		}
	}

	this.hasEkcalWidget = function(entry) {
		if(entry) {
			var iframes = entry.getElementsByTagName("iframe");
			for (var i = 0; i < iframes.length; i++) {
				var src = iframes[i].src;
				if (src != null && src.indexOf(this.ekcalWidgetSrcSubString) >= 0) {
					return true;
				}
			}
		}
		return false;
	};


	this.processUL = function(ul) {
		var ulElements = new Array();

		var lis = ul.getElementsByTagName("li");
		if(lis && lis.length > 0) {

			for(var i = 0; i < lis.length; i++) {

				var buf = this.parseHTMLForEscapedText(lis[i]);

				var trimmed = EkcalTrim(buf);
				if(trimmed) {
					ulElements.push(trimmed);
				}
			}

		}

		//console.log(ulElements);
		return ulElements;
	};

	/**
	 * nodes - element ul lun grupa elementów
	 */
	this.generateWidget = function(nodes, items, title, url, index, prevWidgetId) {
		//console.log("buttons");
		//console.log(items);
		//console.log(ulElement);
		var widgetId = null;
		for(var i = 0; i < 1000; i++) {
			widgetId = 'r'+EkcalRandomString();
			if(EkcalElementMap[widgetId] == null) {
				break;
			}
		}
		if (widgetId == null) {
			widgetId = EkcalReplaceWhitespaces(title, '_') + index;
		}
		EkcalElementMap[widgetId] = nodes;
		if (prevWidgetId != null) {
			EkcalElementMap[prevWidgetId+"next"] = widgetId;
			EkcalElementMap[widgetId+"prev"] = prevWidgetId;
		}
		
		var xmlWriter = new EkcalXMLWriter("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
		xmlWriter.openTag("recipe");
		xmlWriter.appendTag("id", widgetId);
		xmlWriter.appendTag("crc", this.computeCrc(items)+"");
		xmlWriter.appendTag("index", index+"");
		xmlWriter.appendTag("title", title);
		xmlWriter.appendTag("url", url);
		xmlWriter.openTag("items");
		for (var i = 0; i < items.length; i++) {
			xmlWriter.appendTag("item", items[i]);
		}
		xmlWriter.closeTag("items");
		xmlWriter.closeTag("recipe");
	
		var xml = xmlWriter.getXML();

		var newTitle = EkcalReplaceWhitespaces(title, '_');
		EkcalMicroAjax(this.buttonUrl, xml);
		return widgetId;
	};
	
	this.computeCrc = function(items) {
		  var crc = 0x12345678;
		  for (var i = 0; i < items.length; i++) {
			  var item = items[i];
			  for (var j = 0; j < item.length; j++) {
				 crc += (item.charCodeAt(j) * j);
			  }
		  }
		  return crc;
		}

	this.insertAfter = function(currentElement, newElement) {

		var nextObj = currentElement.nextSibling;

		if(nextObj) {
			currentElement.parentNode.insertBefore(newElement, nextObj);
		} else {
			currentElement.parentNode.appendChild(newElement);
		}

	};

	this.getElementsByClassNameL = function(searchClass, node, tag) {

		var classElements = new Array();
		if(node == null)
			node = document;

		if(searchClass == null) {
			if(tag != null)
				var result = node.getElementsByTagName(tag);

			return result;
		}

		if(tag == null)
			tag = '*';

		var els = node.getElementsByTagName(tag);
		var elsLen = els.length;
		var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
		for( i = 0, j = 0; i < elsLen; i++) {
			if(pattern.test(els[i].className)) {
				classElements[j] = els[i];
				j++;
			}
		}
		return classElements;
	};

	/*
	 // /////////
	 // isdefined v1.0
	 //
	 // Check if a javascript variable has been defined.
	 //
	 // Author : Jehiah Czebotar
	 // Website: http://www.jehiah.com
	 // Usage  : alert(isdefined('myvar'));
	 // /////////
	 */
	this.isdefined = function(variable) {
		return ( typeof (window[variable]) == "undefined") ? false : true;
	};
	
	/**
	 * innerText
	 *
	 */
	this.hasInnerText = (document.getElementsByTagName("body")[0].innerText != undefined) ? true : false;

	this.parseHTMLForEscapedText = function(element) {
		var result = "";

		if(element) {
			if(this.hasInnerText) {
				result = element.innerText;
			} else {
				result = element.textContent;
			}
		}

		if(result) {
			result = this.escapeApostrophe(result);
		}

		return result;
	};

	this.parseHTMLForText = function(element) {
		var result = "";
		if(element) {
			if(element.nodeType == 1) {

				if(this.hasInnerText) {
					result = element.innerText;
				} else {
					result = element.textContent;
				}
			} else if(element.nodeType == 3) {
				result = element.nodeValue;
			}
		}
		return result;
	};

	this.escapeApostrophe = function(str) {
		var result = str.replace(/\'/gi, "\\'");
		return result;
	};

	this.IsSet = function(variable) {
		return ( typeof (variable) != 'undefined');
	};
}

var EkcalBlogWidgetInstance = new BlogProcessor("blogger");
EkcalBlogWidgetInstance.Init();

var EkcalEventDocumentReady = (function(){    

    var readyList,
        DOMContentLoaded,
        class2type = {};
        class2type["[object Boolean]"] = "boolean";
        class2type["[object Number]"] = "number";
        class2type["[object String]"] = "string";
        class2type["[object Function]"] = "function";
        class2type["[object Array]"] = "array";
        class2type["[object Date]"] = "date";
        class2type["[object RegExp]"] = "regexp";
        class2type["[object Object]"] = "object";

    var ReadyObj = {
        // Is the DOM ready to be used? Set to true once it occurs.
        isReady: false,
        // A counter to track how many items to wait for before
        // the ready event fires. See #6781
        readyWait: 1,
        // Hold (or release) the ready event
        holdReady: function( hold ) {
            if ( hold ) {
                ReadyObj.readyWait++;
            } else {
                ReadyObj.ready( true );
            }
        },
        // Handle when the DOM is ready
        ready: function( wait ) {
            // Either a released hold or an DOMready/load event and not yet ready
            if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) {
                // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
                if ( !document.body ) {
                    return setTimeout( ReadyObj.ready, 1 );
                }

                // Remember that the DOM is ready
                ReadyObj.isReady = true;
                // If a normal DOM Ready event fired, decrement, and wait if need be
                if ( wait !== true && --ReadyObj.readyWait > 0 ) {
                    return;
                }
                // If there are functions bound, to execute
                readyList.resolveWith( document, [ ReadyObj ] );

                // Trigger any bound ready events
                //if ( ReadyObj.fn.trigger ) {
                //  ReadyObj( document ).trigger( "ready" ).unbind( "ready" );
                //}
            }
        },
        bindReady: function() {
            if ( readyList ) {
                return;
            }
            readyList = ReadyObj._Deferred();

            // Catch cases where $(document).ready() is called after the
            // browser event has already occurred.
            if ( document.readyState === "complete" ) {
                // Handle it asynchronously to allow scripts the opportunity to delay ready
                return setTimeout( ReadyObj.ready, 1 );
            }

            // Mozilla, Opera and webkit nightlies currently support this event
            if ( document.addEventListener ) {
                // Use the handy event callback
                document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
                // A fallback to window.onload, that will always work
                window.addEventListener( "load", ReadyObj.ready, false );

            // If IE event model is used
            } else if ( document.attachEvent ) {
                // ensure firing before onload,
                // maybe late but safe also for iframes
                document.attachEvent( "onreadystatechange", DOMContentLoaded );

                // A fallback to window.onload, that will always work
                window.attachEvent( "onload", ReadyObj.ready );

                // If IE and not a frame
                // continually check to see if the document is ready
                var toplevel = false;

                try {
                    toplevel = window.frameElement == null;
                } catch(e) {}

                if ( document.documentElement.doScroll && toplevel ) {
                    doScrollCheck();
                }
            }
        },
        _Deferred: function() {
            var // callbacks list
                callbacks = [],
                // stored [ context , args ]
                fired,
                // to avoid firing when already doing so
                firing,
                // flag to know if the deferred has been cancelled
                cancelled,
                // the deferred itself
                deferred  = {

                    // done( f1, f2, ...)
                    done: function() {
                        if ( !cancelled ) {
                            var args = arguments,
                                i,
                                length,
                                elem,
                                type,
                                _fired;
                            if ( fired ) {
                                _fired = fired;
                                fired = 0;
                            }
                            for ( i = 0, length = args.length; i < length; i++ ) {
                                elem = args[ i ];
                                type = ReadyObj.type( elem );
                                if ( type === "array" ) {
                                    deferred.done.apply( deferred, elem );
                                } else if ( type === "function" ) {
                                    callbacks.push( elem );
                                }
                            }
                            if ( _fired ) {
                                deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
                            }
                        }
                        return this;
                    },

                    // resolve with given context and args
                    resolveWith: function( context, args ) {
                        if ( !cancelled && !fired && !firing ) {
                            // make sure args are available (#8421)
                            args = args || [];
                            firing = 1;
                            try {
                                while( callbacks[ 0 ] ) {
                                    callbacks.shift().apply( context, args );//shifts a callback, and applies it to document
                                }
                            }
                            finally {
                                fired = [ context, args ];
                                firing = 0;
                            }
                        }
                        return this;
                    },

                    // resolve with this as context and given arguments
                    resolve: function() {
                        deferred.resolveWith( this, arguments );
                        return this;
                    },

                    // Has this deferred been resolved?
                    isResolved: function() {
                        return !!( firing || fired );
                    },

                    // Cancel
                    cancel: function() {
                        cancelled = 1;
                        callbacks = [];
                        return this;
                    }
                };

            return deferred;
        },
        type: function( obj ) {
            return obj == null ?
                String( obj ) :
                class2type[ Object.prototype.toString.call(obj) ] || "object";
        }
    }
    // The DOM ready check for Internet Explorer
    function doScrollCheck() {
        if ( ReadyObj.isReady ) {
            return;
        }

        try {
            // If IE is used, use the trick by Diego Perini
            // http://javascript.nwbox.com/IEContentLoaded/
            document.documentElement.doScroll("left");
        } catch(e) {
            setTimeout( doScrollCheck, 1 );
            return;
        }

        // and execute any waiting functions
        ReadyObj.ready();
    }
    // Cleanup functions for the document ready method
    if ( document.addEventListener ) {
        DOMContentLoaded = function() {
            document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
            ReadyObj.ready();
        };

    } else if ( document.attachEvent ) {
        DOMContentLoaded = function() {
            // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
            if ( document.readyState === "complete" ) {
                document.detachEvent( "onreadystatechange", DOMContentLoaded );
                ReadyObj.ready();
            }
        };
    }
    function ready( fn ) {
        // Attach the listeners
        ReadyObj.bindReady();

        var type = ReadyObj.type( fn );

        // Add the callback
        readyList.done( fn );//readyList is result of _Deferred()
    }
    return ready;
})();

EkcalEventDocumentReady(function() {
	EkcalBlogWidgetInstance.process();
});
